package com.atguigu.tingshu.album.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@Transactional(rollbackFor = Exception.class)
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private VodUploadClient uploadClient;

    @Autowired
    private VodConstantProperties vodConstantProperties;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    /**
     * 上传声音
     *
     * @param file
     * @return
     */
    @SneakyThrows
    @Override
    public JSONObject uploadTrack(MultipartFile file) {
        //将文件保存到临时目录
        String filePath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);

        //构造上传请求对象
        VodUploadRequest request = new VodUploadRequest();
        request.setMediaFilePath(filePath);

        //调用上传
        VodUploadResponse response = uploadClient.upload("ap-guangzhou", request);

        //封装文件信息
        JSONObject result = new JSONObject();
        //封装文件的唯一标识
        result.put("mediaFileId", response.getFileId());
        //封装文件的播放地址
        result.put("mediaUrl", response.getMediaUrl());

        return result;
    }

    /**
     * 保存声音
     *
     * @param trackInfoVo
     */
    @Override
    public void saveTrackInfo(TrackInfoVo trackInfoVo) {

        //保存声音
        //属性转移
        TrackInfo trackInfo = new TrackInfo();
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        //获取用户id
        Long userId = AuthContextHolder.getUserId();
        //补全属性:设置用户id
        trackInfo.setUserId(userId);
        //补全属性:设置声音在专辑中的排序
        //查询声音
        Integer maxNum = trackInfoMapper.selectMaxOrderNum(trackInfo.getAlbumId());
        trackInfo.setOrderNum(++maxNum);
        //补全属性:设置审核状态
        trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
        //补全属性:设置声音来源
        trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        //获取声音数据
        MediaInfo mediaInfo = getMediaInfo(trackInfo.getMediaFileId());
        //设置声音的基础信息
        MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
        trackInfo.setMediaType(basicInfo.getType());
        trackInfo.setMediaUrl(basicInfo.getMediaUrl());
        //设置声音的元信息
        MediaMetaData metaData = mediaInfo.getMetaData();
        trackInfo.setMediaSize(metaData.getSize());
        trackInfo.setMediaDuration(new BigDecimal(metaData.getDuration().toString()));
        //保存声音
        int i = trackInfoMapper.insert(trackInfo);
        if (i < 0) {
            throw new GuiguException(201, "新增声音失败");
        }

        //初始化声音的统计数据
        initTrackStat(trackInfo.getId());
    }

    @SneakyThrows
    private MediaInfo getMediaInfo(String mediaFileId) {
        // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
        // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
        // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
        Credential cred = new Credential(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
        // 实例化一个http选项，可选的，没有特殊需求可以跳过
        HttpProfile httpProfile = new HttpProfile();
        httpProfile.setEndpoint("vod.tencentcloudapi.com");
        // 实例化一个client选项，可选的，没有特殊需求可以跳过
        ClientProfile clientProfile = new ClientProfile();
        clientProfile.setHttpProfile(httpProfile);
        // 实例化要请求产品的client对象,clientProfile是可选的
        VodClient client = new VodClient(cred, "ap-guangzhou", clientProfile);
        // 实例化一个请求对象,每个接口都会对应一个request对象
        DescribeMediaInfosRequest req = new DescribeMediaInfosRequest();
        String[] fileIds1 = {mediaFileId};
        req.setFileIds(fileIds1);

        // 返回的resp是一个DescribeMediaInfosResponse的实例，与请求对象对应
        DescribeMediaInfosResponse resp = client.DescribeMediaInfos(req);
        MediaInfo[] mediaInfoSet = resp.getMediaInfoSet();
        if (mediaInfoSet != null && mediaInfoSet.length > 0) {
            return mediaInfoSet[0];
        } else {
            throw new GuiguException(201, "获取声音信息失败");
        }


    }

    /**
     * 初始化声音的统计数据
     *
     * @param trackId
     */
    private void initTrackStat(Long trackId) {
        TrackStat trackStat = new TrackStat();
        trackStat.setTrackId(trackId);
        trackStat.setStatNum(0);

        //初始化播放量
        trackStat.setStatType(SystemConstant.TRACK_STAT_PLAY);
        int i = trackStatMapper.insert(trackStat);
        if (i < 0) {
            throw new GuiguException(201, "初始化声音的统计数据失败");
        }

        //初始化收藏量
        trackStat.setId(null);
        trackStat.setStatType(SystemConstant.TRACK_STAT_COLLECT);
        i = trackStatMapper.insert(trackStat);
        if (i < 0) {
            throw new GuiguException(201, "初始化声音的统计数据失败");
        }

        //初始化收藏量
        trackStat.setId(null);
        trackStat.setStatType(SystemConstant.TRACK_STAT_PRAISE);
        i = trackStatMapper.insert(trackStat);
        if (i < 0) {
            throw new GuiguException(201, "初始化声音的统计数据失败");
        }

        //初始化收藏量
        trackStat.setId(null);
        trackStat.setStatType(SystemConstant.TRACK_STAT_COMMENT);
        i = trackStatMapper.insert(trackStat);
        if (i < 0) {
            throw new GuiguException(201, "初始化声音的统计数据失败");
        }
    }

    /**
     * 分页查询声音
     *
     * @param trackInfoQuery
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public IPage<TrackListVo> findUserTrackPage(TrackInfoQuery trackInfoQuery, Long pageNum, Long pageSize) {
        trackInfoQuery.setUserId(AuthContextHolder.getUserId());
        return trackInfoMapper.findUserTrackPage(trackInfoQuery, new Page<TrackListVo>(pageNum, pageSize));
    }

    /**
     * 删除声音
     *
     * @param trackId
     */
    @Override
    public void removeTrackInfo(Long trackId) {
        //删除声音
        //获取用户id
        Long userId = AuthContextHolder.getUserId();
        int i = trackInfoMapper.delete(
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getUserId, userId).eq(TrackInfo::getId, trackId));
        if (i <= 0) {
            throw new GuiguException(201, "删除失败");
        }

        //删除声音的统计数据
        i = trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
        if (i < 0) {
            throw new GuiguException(201, "删除失败");
        }
    }

    /**
     * 更新声音
     *
     * @param trackId
     * @param trackInfoVo
     */
    @Override
    public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {
        //获取洪用户id
        Long userId = AuthContextHolder.getUserId();
        TrackInfo trackInfo = trackInfoMapper.selectOne(
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getUserId, userId).eq(TrackInfo::getId, trackId));
        if (trackInfo == null) {
            return;
        }
        //获取旧的文件id
        String mediaFileId = trackInfoVo.getMediaFileId();
        //属性转移
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        //判断是否更换了音频文件
        if (!trackInfo.getMediaFileId().equals(trackInfoVo.getMediaFileId())) {
            MediaInfo mediaInfo = getMediaInfo(trackInfo.getMediaFileId());
            //设置声音的基础信息
            MediaBasicInfo basicInfo = mediaInfo.getBasicInfo();
            trackInfo.setMediaType(basicInfo.getType());
            trackInfo.setMediaUrl(basicInfo.getMediaUrl());
            //设置声音的元信息
            MediaMetaData metaData = mediaInfo.getMetaData();
            trackInfo.setMediaSize(metaData.getSize());
            trackInfo.setMediaDuration(new BigDecimal(metaData.getDuration().toString()));
            //补全属性:设置审核状态
            trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
            //补全属性:设置声音来源
            trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
        }
        trackInfoMapper.update(trackInfo,
                new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getId, trackId));
    }

    /**
     * 分页查询声音列表
     *
     * @param albumId
     * @param pageNum
     * @param pageSize
     * @return
     */
    @SneakyThrows
    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Integer pageNum, Integer pageSize) {
        //异步查询专辑信息
        CompletableFuture<AlbumInfo> future1 = CompletableFuture.supplyAsync(() -> {
            return albumInfoMapper.selectById(albumId);
        }, threadPoolExecutor);
        //异步查询声音分页信息
        CompletableFuture<IPage<AlbumTrackListVo>> future2 = CompletableFuture.supplyAsync(() -> {
            IPage<AlbumTrackListVo> albumTrackListVoPage =
                    trackInfoMapper.findAlbumTrackPage(albumId, new Page<>(pageNum, pageSize));
            return albumTrackListVoPage;
        }, threadPoolExecutor);
        CompletableFuture.allOf(future1, future2).join();
        //获取专辑信息
        AlbumInfo albumInfo = future1.get();
        //获取专辑的全部声音
        IPage<AlbumTrackListVo> albumTrackListVoPage = future2.get();
        List<AlbumTrackListVo> albumTrackListVo = albumTrackListVoPage.getRecords();
        //专辑的收费类型:0101-免费、0102-vip免费、0103-付费
        String payType = albumInfo.getPayType();
        switch (payType) {
            //免费
            case SystemConstant.ALBUM_PAY_TYPE_FREE -> {
                return albumTrackListVoPage;
            }
            //vip免费
            case SystemConstant.ALBUM_PAY_TYPE_VIPFREE -> {
                //获取用户的VIP信息
                Integer isVip = AuthContextHolder.getIsVip();
                Long vipExpireTime = AuthContextHolder.getVipExpireTime();
                if (isVip == 1 && vipExpireTime > System.currentTimeMillis()) {
                    //是vip
                    return albumTrackListVoPage;
                } else {
                    //不是vip
                    return makeShowPaidMark(albumInfo, albumTrackListVo, albumTrackListVoPage);
                }
            }
            //收费
            case SystemConstant.ALBUM_PAY_TYPE_REQUIRE -> {
                //专辑的价格类型:0201-单集 0202-整专辑
                String priceType = albumInfo.getPriceType();
                if (priceType.equals(SystemConstant.ALBUM_PRICE_TYPE_ALL)) {
                    //整个专辑收费,是否购买过
                    Boolean isBuy = userInfoFeignClient.isBuyAlbum(AuthContextHolder.getUserId(), albumId);
                    if (isBuy) {
                        return albumTrackListVoPage;
                    } else {
                        return makeShowPaidMark(albumInfo, albumTrackListVo, albumTrackListVoPage);
                    }
                } else {
                    //单个声音收费
                    Map<String, Integer> buyTrackMap =
                            userInfoFeignClient.getBuyTrackList(AuthContextHolder.getUserId(), albumId);
                    //专辑的免费声音集数
                    Integer tracksForFree = albumInfo.getTracksForFree();
                    List<AlbumTrackListVo> trackVoList = albumTrackListVo.stream().map(vo -> {
                                if (vo.getOrderNum() > tracksForFree &&
                                        buyTrackMap.get(vo.getTrackId().toString()) == null) {
                                    //没有购买,将表示改为需要支付
                                    vo.setIsShowPaidMark(true);
                                }
                                return vo;
                            }
                    ).collect(Collectors.toList());
                    return albumTrackListVoPage.setRecords(trackVoList);
                }
            }
            default -> {return albumTrackListVoPage;}
        }

    }

    private IPage<AlbumTrackListVo> makeShowPaidMark(AlbumInfo albumInfo,
                                                     List<AlbumTrackListVo> albumTrackListVo,
                                                     IPage<AlbumTrackListVo> albumTrackListVoPage) {
        //专辑的免费声音集数
        Integer tracksForFree = albumInfo.getTracksForFree();
        //将不是免费集数的转为收费
        List<AlbumTrackListVo> trackVoList = albumTrackListVo.stream().map(vo -> {
            if (vo.getOrderNum() > tracksForFree) {
                vo.setIsShowPaidMark(true);
            }
            return vo;
        }).collect(Collectors.toList());
        return albumTrackListVoPage.setRecords(trackVoList);
    }
}
